package com.bstek.uflo.designer.serializer.impl;

import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.FastDateFormat;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import com.bstek.uflo.designer.model.AssignmentType;
import com.bstek.uflo.designer.model.SwimlaneAssignee;
import com.bstek.uflo.designer.model.node.Node;
import com.bstek.uflo.designer.model.process.Process;
import com.bstek.uflo.designer.model.process.Swimlane;
import com.bstek.uflo.designer.serializer.ProcessSerializer;
import com.bstek.uflo.designer.serializer.ShapeSerializer;

/**
 * @author matt.yao@bstek.com
 * @since 1.0
 */
@Service(ProcessSerializer.BEAN_ID)
public class DefaultProcessSerializer implements ProcessSerializer, ApplicationContextAware {

	private Collection<ShapeSerializer> shapeSerializerColl;

	public Document execute(Process process) throws Exception {
		Assert.notNull(process);
		Document document = DocumentHelper.createDocument();
		Element processElement = document.addElement("uflo-process");
		String name = process.getName();
		if (StringUtils.hasText(name)) {
			processElement.addAttribute("name", name);
		}
		String key = process.getKey();
		if (StringUtils.hasText(key)) {
			processElement.addAttribute("key", key);
		}
		String startProcessUrl = process.getStartProcessUrl();
		if (StringUtils.hasText(startProcessUrl)) {
			processElement.addAttribute("start-process-url", startProcessUrl);
		}
		String eventHandlerBean = process.getEventHandlerBean();
		if (StringUtils.hasText(eventHandlerBean)) {
			processElement.addAttribute("event-handler-bean", eventHandlerBean);
		}
		String categoryId = process.getCategoryId();
		if (StringUtils.hasText(categoryId)) {
			processElement.addAttribute("category-id", categoryId);
		}
		Date effectDate = process.getEffectDate();
		if (effectDate != null) {
			processElement.addAttribute("effect-date", FastDateFormat.getInstance("yyyy-MM-dd HH:mm:ss").format(effectDate));
		}
		String desc = process.getDescription();
		if (StringUtils.hasText(desc)) {
			Element descriptionElement = processElement.addElement("description");
			descriptionElement.addText(desc);
		}
		this.serializerSwimlane(process, processElement);
		this.serializerNode(process, processElement);
		return document;
	}

	private void serializerSwimlane(Process process, Element processElement) {
		List<Swimlane> swimlanes = process.getSwimlanes();
		if (swimlanes != null) {
			for (Swimlane swimlane : swimlanes) {
				String assignmentType = swimlane.getAssignmentType();
				if (StringUtils.hasText(assignmentType)) {
					Element swimlaneElement = processElement.addElement("swimlane");
					swimlaneElement.addAttribute("name", swimlane.getName());
					swimlaneElement.addAttribute("assignment-type", swimlane.getAssignmentType());
					if (assignmentType.equals(AssignmentType.Expression.name())) {
						swimlaneElement.addAttribute("expression", swimlane.getExpression());
					} else if (assignmentType.equals(AssignmentType.Handler.name())) {
						swimlaneElement.addAttribute("assignment-handler-bean", swimlane.getAssignmentHandlerBean());
					} else if (assignmentType.equals(AssignmentType.Assignee.name())) {
						List<SwimlaneAssignee> swimlaneAssignees = swimlane.getSwimlaneAssignees();
						if (swimlaneAssignees != null) {
							for (SwimlaneAssignee swimlaneAssignee : swimlaneAssignees) {
								Element swimlaneAssigneeElement = swimlaneElement.addElement("assignee");
								swimlaneAssigneeElement.addAttribute("id", swimlaneAssignee.getId());
								swimlaneAssigneeElement.addAttribute("name", swimlaneAssignee.getName());
								swimlaneAssigneeElement.addAttribute("provider-id", swimlaneAssignee.getProviderId());
							}
						}
					}
				}
			}
		}
	}

	private void serializerNode(Process process, Element processElement) {
		List<Node> nodes = process.getNodes();
		for (Node node : nodes) {
			for (ShapeSerializer shapeSerializer : shapeSerializerColl) {
				if (shapeSerializer.support(node)) {
					shapeSerializer.execute(processElement, node);
				}
			}
		}
	}

	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.shapeSerializerColl = applicationContext.getBeansOfType(ShapeSerializer.class).values();
	}
}
